package com.ale.oxetool.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.lang.UsesUnsafeJava;
import org.springframework.cglib.core.CollectionUtils;
import org.springframework.stereotype.Service;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wgs on 2016/4/3.
 * Wrap data in list into jqgrid style json
 */

@Service
@SuppressWarnings("unchecked")
public class GridServiceImpl implements GridService {

    @Autowired
    private PhoneService phoneService;

    private static Logger log = LoggerFactory.getLogger(GridServiceImpl.class);

    private Object getObjectProp(String propertyName, Object object) throws RuntimeException {
        int dotIndex = propertyName.indexOf(".");
        if (-1 != dotIndex) {
            return getObjectProp(propertyName.substring(dotIndex + 1), getObjectProp(propertyName.substring(0, dotIndex), object));
        }

        if (object instanceof Map) {
            return ((Map) object).get(propertyName);
        }
        if (null == object) {
            return null;
        }
        try {
            PropertyDescriptor[] pds = Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                if (pd.getReadMethod() != null && propertyName.equals(pd.getName())) {
                    return pd.getReadMethod().invoke(object);
                }
            }
        } catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    @UsesUnsafeJava
    private List filterList(List list, String filters) {
        if (null == filters) return list;

        // filters, example: filters: {"groupOp":"AND","rules":[{"field":"name","op":"cn","data":"33009"}]}
        Map mapFilters;
        try {
            mapFilters = new ObjectMapper().readValue(filters, Map.class);
        } catch (IOException e) {
            log.warn("filters: {} format is not correct!");
            e.printStackTrace();
            return list;
        }

        //groupOp us AND for now
//        String groupOp = (String) mapFilters.get("groupOp");
        ArrayList<HashMap> rules = (ArrayList<HashMap>) mapFilters.get("rules");
        if (null == rules || rules.isEmpty()
//                || null == groupOp
        ) {
            return list;
        }

        List lst = new ArrayList();
        for (Object o : list) {

            boolean isSatisfied = true;
            for (HashMap rule : rules) {
                String field = (String) rule.get("field");
//                String op = rule.get("op");
                String data = (String) rule.get("data");
                // current just treat op as contain
                Object value = getObjectProp(field, o);
                if (value instanceof Boolean) {
                    if (!value.toString().equals(data)) { // need to be removed
                        isSatisfied = false;
                        break;
                    }
                } else if (value instanceof Integer) {
                    if (!value.equals(Integer.parseInt(data))) {
                        isSatisfied = false;
                        break;
                    }
                } else if (value instanceof String) {
                    if (!((String) value).contains(data)) {
                        isSatisfied = false;
                        break;
                    }
                } else {
                    if (null == value) {
                        log.warn("field {} value for object {} is null, can't handdled!", field, o);
                    } else {
                        log.warn("field {} value for object {} is instance of {}, can't handdled!", field, o, value.getClass().getName());
                    }
                    isSatisfied = false;
                    break;
                }
            }

            if (isSatisfied) {
                lst.add(o);
            }
        }
        return lst;
    }

    @Override
    public Map<String, Object> getPagedList(List list, String colProps, String idProp, String sord, String sidx,
                                            int page, int rows, String filters) {
        return getPagedList(list, colProps, idProp, sord, sidx, page, rows, filters, new HashMap<>());
    }

    @UsesUnsafeJava
    public Map<String, Object> getPagedList(List list, String colProps, String idProp, String sord, String sidx,
                                            int page, int rows, String filters, Map<String, Object> costumeUserData) {
        Map<String, Object> root = new HashMap<>();
        // filter the records first and then page it
        list = filterList(list, filters);

        int records = list.size();
        int total;
        if (rows > 0) {
            total = (int) Math.ceil((float) records / rows);
        } else {
            page = 1;
            total = 1;
            rows = records;
        }
        root.put("total", total);
        root.put("page", page);
        root.put("records", records);

        log.debug("Before sort list: {}", list);
        // sort first
        list.sort((o1, o2) -> {
            Object prop1 = getObjectProp(sidx, o1);
            Object prop2 = getObjectProp(sidx, o2);
            int compareResult = 0;
            if (prop1 instanceof Boolean) {
                assert prop2 != null;
                compareResult = ((Boolean) prop1).compareTo((Boolean) prop2);
            } else if (prop1 instanceof String) {
                assert prop2 != null;
                compareResult = ((String) prop1).compareTo((String) prop2);
            }
            if ("desc".equals(sord)) {
                compareResult = -compareResult;
            }
            return compareResult;
        });

        //pager
        list = list.subList((page - 1) * rows, rows);
        String[] propList = colProps.split("\\s*,\\s*");

        root.put("rows", CollectionUtils.transform(list, o -> {
            Map<String, Object> row = new HashMap<>();
            row.put("id", getObjectProp(idProp, o));

            //populate cell list
            List<Object> cellList = new ArrayList<>();
            for (String prop : propList) {
                cellList.add(getObjectProp(prop, o));
            }
            row.put("cell", cellList);
            return row;
        }));

        Map<String, Object> userData = new HashMap<>(costumeUserData);
        root.put("userdata", userData);

        return root;
    }

    @Override
    public Map<String, Object> getPagedPhones(String colProps, String idProp, String sord, String sidx,
                                              int page, int rows, String filters) throws Exception {
        return getPagedList(phoneService.getPhones(), colProps, idProp, sord, sidx, page, rows, filters);
    }

}

